package org.example.java.util.concurrency.advanced_object;//: concurrency/ExchangerDemo.java

import org.example.util.BasicGenerator;
import org.example.util.Generator;

import java.util.List;
import java.util.concurrent.*;

public class ExchangerDemo {
  static class ExchangerProducer<T> implements Runnable {
    private Generator<T> generator;
    private Exchanger<List<T>> exchanger;
    private List<T> holder;

    ExchangerProducer(Exchanger<List<T>> exchg,
                      Generator<T> gen, List<T> holder) {
      exchanger = exchg;
      generator = gen;
      this.holder = holder;
    }

    public void run() {
      try {
        while (!Thread.interrupted()) {
          for (int i = 0; i < ExchangerDemo.size; i++)
            holder.add(generator.next());
          // Exchange full for empty:
          holder = exchanger.exchange(holder);
        }
      } catch (InterruptedException e) {
        // OK to terminate this way.
      }
    }
  }

  static class ExchangerConsumer<T> implements Runnable {
    private Exchanger<List<T>> exchanger;
    private List<T> holder;
    private volatile T value;

    ExchangerConsumer(Exchanger<List<T>> ex, List<T> holder) {
      exchanger = ex;
      this.holder = holder;
    }

    public void run() {
      try {
        while (!Thread.interrupted()) {
          holder = exchanger.exchange(holder);
          for (T x : holder) {
            value = x; // Fetch out value
            holder.remove(x); // OK for CopyOnWriteArrayList
          }
        }
      } catch (InterruptedException e) {
        // OK to terminate this way.
      }
      System.out.println("Final value: " + value);
    }
  }

  static int size = 10;
  static int delay = 5; // Seconds

  public static void main(String[] args) throws Exception {
    if (args.length > 0)
      size = new Integer(args[0]);
    if (args.length > 1)
      delay = new Integer(args[1]);
    ExecutorService exec = Executors.newCachedThreadPool();
    Exchanger<List<Fat>> xc = new Exchanger<List<Fat>>();
    List<Fat>
        producerList = new CopyOnWriteArrayList<Fat>(),
        consumerList = new CopyOnWriteArrayList<Fat>();
    exec.execute(new ExchangerProducer<Fat>(xc, BasicGenerator.create(Fat.class), producerList));
    exec.execute(new ExchangerConsumer<Fat>(xc, consumerList));
    TimeUnit.SECONDS.sleep(delay);
    exec.shutdownNow();
  }
} /* Output: (Sample)
Final value: Fat id: 29999
*///:~
